#include "merkelTree.h"

// using namespace boost::threadpool;
// using namespace boost::placeholders;

// void calc_hash_of_data_with_sha256(const std::string &data, std::string &res_hash)
// {
//     unsigned char hash[SHA256_DIGEST_LENGTH];
//     SHA256_CTX sha256;

//     SHA256_Init(&sha256);
//     SHA256_Update(&sha256, data.c_str(), data.size());
//     SHA256_Final(hash, &sha256);
//     // std::string ret;
//     for (size_t i = 0; i < SHA256_DIGEST_LENGTH; i++)
//     {
//         res_hash += hash[i];
//     }
//     // std::cout << data.size() << std::endl;
// }

void calc_hash_of_data_with_sha256(const char *data, int length, std::string &res_hash)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;

    SHA256_Init(&sha256);
    SHA256_Update(&sha256, data, length);
    SHA256_Final(hash, &sha256);
    // std::string ret;
    for (size_t i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        res_hash += hash[i];
    }
    // std::cout << length << std::endl;
}

void single_thread_cal_merkel_hash(std::string &content, std::string &hash)
{
    auto pos1 = std::chrono::steady_clock::now();

    merkelTree hash_tree;
    std::string root_hash;

    for (size_t i = 0; i < 32; i++)
    {
        cal_sub_merkel_hash(content, i, &(hash_tree.nodes[i]));
        root_hash.append(hash_tree.nodes[i]->node_hash);
    }
    calc_hash_of_data_with_sha256(root_hash.c_str(), root_hash.size(), hash);

    auto pos2 = std::chrono::steady_clock::now();
    std::chrono::duration<double> dur1 = pos2 - pos1;
    std::cout << dur1.count() << std::endl;
}

void muti_thread_cal_merkel_hash(std::string &content, std::string &hash)
{
    auto pos1 = std::chrono::steady_clock::now();
    std::vector<std::thread *> tp;

    merkelTree hash_tree;
    std::string root_hash;

    for (size_t i = 0; i < 32; i++)
    {
        std::thread *t = new std::thread(cal_sub_merkel_hash, std::ref(content), i, &(hash_tree.nodes[i]));
        tp.emplace_back(t);
    }
    for (auto &it : tp)
        it->join();
    // tp.clear();
    // for (size_t i = 16; i < 32; i++)
    // {
    //     std::thread *t = new std::thread(cal_sub_merkel_hash, std::ref(content), i, &(hash_tree.nodes[i]));
    //     tp.emplace_back(t);
    // }
    // for (auto &it : tp)
    //     it->join();

    for (size_t i = 0; i < 32; i++)
    {
        root_hash.append(hash_tree.nodes[i]->node_hash);
    }
    calc_hash_of_data_with_sha256(root_hash.c_str(), root_hash.size(), hash);

    auto pos2 = std::chrono::steady_clock::now();
    std::chrono::duration<double> dur1 = pos2 - pos1;
    std::cout << dur1.count() << std::endl;
}

void cal_sub_merkel_hash(std::string &content, int num, node **sub_root)
{
    // auto pos1 = std::chrono::steady_clock::now();

    node *tmp_root = new node;
    std::string tmp_hash;
    for (size_t i = 0; i < 32; i++)
    {
        const char *content_4k = content.c_str() + (((num << 5) | i) << 12);
        calc_hash_of_data_with_sha256(content_4k, (1 << 12), tmp_root->leaf_hashs[i]);
        tmp_hash.append(tmp_root->leaf_hashs[i]);
    }
    if (tmp_hash.size() != 32 * 32)
    {
        std::cout << "string append error" << std::endl;
        return;
    }

    calc_hash_of_data_with_sha256(tmp_hash.c_str(), tmp_hash.size(), tmp_root->node_hash);
    *sub_root = tmp_root;

    // auto pos2 = std::chrono::steady_clock::now();
    // std::chrono::duration<double> dur1 = pos2 - pos1;
    // std::cout << dur1.count() << std::endl;
}

void print_hash(std::string &hash)
{
    std::string NewString = "";
    char buf[2];
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        // snprintf(buf, 2, "%02x", hash[i]);
        // NewString = NewString + buf;
        if ((int)hash[i] < 0)
            std::cout << 256 + (int)hash[i] << " ";
        else
            std::cout << (int)hash[i] << " ";
    }
    std::cout << NewString << std::endl;
}

int main(int argc, char **argv)
{
    std::string content;
    // for (size_t i = 0; i < 1024 * 1024 * 4; i++)
    //     content += '\0';
    std::ifstream ifile("/tmp/test.txt");
    //将文件读入到ostringstream对象buf中
    std::ostringstream buf;
    char ch;
    while (buf && ifile.get(ch))
        buf.put(ch);
    content = buf.str();
    std::cout << "conent length: " << content.size() << std::endl;

    // std::ofstream ofile("/home/mcloud/test_5M", std::ofstream::trunc);
    // ofile << content;
    // ofile.close();
    std::string hash;

    for (size_t i = 0; i < 1; i++)
    {
        hash = "";
        single_thread_cal_merkel_hash(content, hash);
    }
    print_hash(hash);

    for (size_t i = 0; i < 3; i++)
    {
        hash = "";
        muti_thread_cal_merkel_hash(content, hash);
    }
    print_hash(hash);

    return 0;
}